package com.jaychen.eureka.registry;

import com.jaychen.eureka.cache.CacheManager;
import com.jaychen.eureka.domain.InstanceInfo;
import com.jaychen.eureka.domain.Lease;
import com.jaychen.eureka.domain.constant.ServerConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author Jay Chen
 * @Date 2020-10-24 08:43
 * @Version 1.0
 */
public abstract class AbstractInstanceRegistry implements InstanceRegistry {

    private static final Logger logger = LoggerFactory.getLogger(AbstractInstanceRegistry.class);

    /**
     * 服务续约缓存
     */
    private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
            = new ConcurrentHashMap<>();

    /**
     * 保证代码指令不会被重排序
     */
    protected volatile CacheManager cacheManager;

    /**
     * 定时器
     */
    private final Timer timer = new Timer("JayChen-Eureka-Cache-Scan-Timer");

    /**
     * 服务注册
     *
     * @param r             - T to register
     * @param leaseDuration 续约时间
     */
    @Override
    public void register(InstanceInfo r, Long leaseDuration) {
        // 根据服务名称获取服务实例集合
        Map<String, Lease<InstanceInfo>> map = registry.get(r.getAppName());
        if (map == null) {
            final ConcurrentHashMap<String, Lease<InstanceInfo>> newMap = new ConcurrentHashMap<>();
            // 防止在添加新的服务实例集群租约时，把已有的的其他线程添加的集群租约给覆盖掉。
            map = registry.putIfAbsent(r.getAppName(), newMap);
            if (map == null) {
                map = newMap;
            }
        }
        //根据instanceId获取实例的租约
        Lease<InstanceInfo> instanceInfoLease = map.get(r.getInstanceId());
        if (instanceInfoLease == null) {
            // 创建新的租约
            instanceInfoLease = new Lease<>(r, leaseDuration);
        } else {
            // 更新时间
            instanceInfoLease.setLastUpdateTimestamp(System.currentTimeMillis());
            // 覆盖状态
            InstanceInfo instanceInfo = instanceInfoLease.getHolder();
            instanceInfo.setInstanceStatus(InstanceInfo.InstanceStatus.UP);
        }
        map.put(r.getInstanceId(), instanceInfoLease);
        registry.put(r.getAppName(), map);
        cacheManager.addToReadOnlyCacheMap(r.getAppName(), map);
        // 缓存过期(readWriteCacheMap)
        invalidateCache(r.getAppName());
        logger.info("lease appName:{}, port:{}, ip:{}", r.getAppName(), r.getPort(), r.getAddress());
    }


    /**
     * 初始化
     */
    @Override
    public synchronized void initializedCache() {
        if (cacheManager == null) {
            cacheManager = new CacheManager(this);
        }
        this.timer.schedule(getCacheUpdateTask(), ServerConstant.CACHE_SCAN_TIMER, ServerConstant.CACHE_SCAN_TIMER);
    }

    /**
     * 定时检测服务租约情况，超过90s 没有续租约，则剔除
     *
     * @return TimerTask
     */
    private TimerTask getCacheUpdateTask() {
        return new TimerTask() {
            @Override
            public void run() {
                for (Map.Entry<String, Map<String, Lease<InstanceInfo>>> stringMapEntry : registry.entrySet()) {
                    Map<String, Lease<InstanceInfo>> map = stringMapEntry.getValue();
                    if (CollectionUtils.isEmpty(map)) {
                        registry.remove(stringMapEntry.getKey());
                        continue;
                    }

                    for (Map.Entry<String, Lease<InstanceInfo>> leaseEntry : stringMapEntry.getValue().entrySet()) {
                        Lease<InstanceInfo> entry = leaseEntry.getValue();
                        long lastUpdateTimestamp = entry.getLastUpdateTimestamp();
                        long currentTimestamp = System.currentTimeMillis();
                        if ((currentTimestamp - lastUpdateTimestamp) / 1000 > Lease.DEFAULT_LEASE_DURATION) {
                            logger.info("lease fail instance ip:{}, port:{}, serviceName:{}", entry.getHolder().getAddress(),
                                    entry.getHolder().getPort(), entry.getHolder().getAppName());
                            map.remove(entry.getHolder().getInstanceId());
                            invalidateCache(entry.getHolder().getAppName());
                        }

                    }
                }
            }
        };

    }

    /**
     * 从注册表获取注册服务信息
     *
     * @param key appName
     * @return Map<String, Lease < InstanceInfo>>
     */
    public Map<String, Lease<InstanceInfo>> generatePayload(String key) {
        return registry.get(key);
    }

    /**
     * 缓存失效
     *
     * @param appName
     */
    public void invalidateCache(String appName) {
        cacheManager.invalidateCache(appName);
    }

    /**
     * 获取注册列表
     */
    public Map<String, Map<String, Lease<InstanceInfo>>> getRegistryList() {
        return cacheManager.getReadOnlyCacheMap();
    }
}
